<template>
	<view class="bg-editor"
		:class="{ expand: expand, 'only-edit': onlyShow === 'edit', 'only-html': onlyShow === 'html' }">
		<!-- 工具栏 -->
		<view class="editor-tools">
			<!-- 撤回 -->
			<view @click="undo" class="tool-box">
				<Icon :size="16">
					<UndoAlt />
				</Icon>
			</view>
			<!-- 复原 -->
			<view @click="redo" class="tool-box divider-r">
				<Icon :size="16">
					<RedoAlt />
				</Icon>
			</view>
			<!-- 样式按钮 -->
			<view @click="setStyle(tool)" v-for="tool in tools" class="tool-box">
				<Icon :size="16">
					<component :is="tool.icon" />
				</Icon>
				<view v-if="tool.list" class="list-box">
					<span @click.stop="setStyle(item)" v-for="item in tool.list" class="item">{{ item.name }}</span>
				</view>
			</view>
			<!-- 全屏 -->
			<view @click="expand = !expand" class="tool-box divider-l">
				<Icon :size="16">
					<ExpandArrowsAlt />
				</Icon>
			</view>
			<!-- 只显示编辑区 -->
			<view @click="changeShow('edit')" class="tool-box">
				<Icon :size="16">
					<WindowMaximizeRegular />
				</Icon>
			</view>
			<!-- 只显示 html 区 -->
			<view @click="changeShow('html')" class="tool-box">
				<Icon :size="16">
					<WindowMaximize />
				</Icon>
			</view>
		</view>

		<view class="editor-main">
			<!-- 编辑区 -->
			<scroll-view @scroll="scroll" :scroll-top="scrollTop" scroll-y class="editor-textarea">
				<textarea :value="inputValue" :focus="focus" :cursor="cursor" @input="input" @blur="blur"
					placeholder="在这里输入正文" maxlength="-1" class="textarea" auto-height />
			</scroll-view>
			<!-- 展示区 -->
			<scroll-view @scroll="scroll" :scroll-top="scrollTop" scroll-y class="editor-html">
				<div v-html="compiledMarkdown" class="markdown-body"></div>
			</scroll-view>
			<!-- 关闭按钮 -->
			<view v-if="onlyShow === 'html'" @click="changeShow('html')" class="close-html">
				<Icon :size="16" color="#666666">
					<Times />
				</Icon>
			</view>
		</view>
	</view>
</template>

<script setup>
	// 迫于找不到合适且兼容 uniapp 的，所以自己写了一个 markdown 编辑器，为了分离出去，我尽量不使用 element 的组件（踩了很多坑的，不容易啊）
	import {
		Icon
	} from '@vicons/utils'
	import {
		UndoAlt,
		RedoAlt,
		ExpandArrowsAlt,
		WindowMaximizeRegular,
		WindowMaximize,
		Times
	} from '@vicons/fa'
	import markdownIt from 'markdown-it'
	import markdownItSub from 'markdown-it-sub'
	import markdownItSup from 'markdown-it-sup'
	import {
		tools
	} from './toolsJson.js'
	// import modal from './modal.vue'
	import toolform from './toolform.vue'
	import {
		ref,
		watch,
		computed
	} from 'vue'
	import {
		nextTick,
		reactive,
		defineComponent
	} from 'vue';

	// 初始化 markdown-it
	var md = markdownIt({
		breaks: true,
		html: true
	})
	// 引入插件 其他插件按需引入：https://github.com/markdown-it/markdown-it
	md.use(markdownItSub)
	md.use(markdownItSup)

	const expand = ref(false) // 全屏控制
	const onlyShow = ref('') // 只显示编辑区或者 html 区 值：edit / html
	// 切换显示模式
	const changeShow = value => {
		if (onlyShow.value === value) {
			onlyShow.value = ''
		} else {
			onlyShow.value = value
		}
	}

	const historys = [] // 操作历史
	let index = 0 // 当前操作位置

	const inputValue = ref('') // textarea 输入的值
	const input = e => {
		inputValue.value = e.detail.value
		putHistory()
	}

	// 记录操作
	const putHistory = () => {
		historys.push(inputValue.value)
		// 防止占用太多内存，只记录10条操作
		index = historys.length - 1
		if (index > 10) {
			historys.shift()
			index--
		}
	}
	// 撤回
	const undo = () => {
		if (index > 0) {
			inputValue.value = historys[index - 1]
			index--
		}
	}
	// 复原
	const redo = () => {
		if (index < historys.length - 1) {
			inputValue.value = historys[index + 1]
			index++
		}
	}

	// 监听容器滚动，实现同步滚动
	let scrollTop = ref(0)
	const scroll = e => {
		scrollTop.value = e.detail.scrollTop
	}

	// 转换 markdown 内容
	const compiledMarkdown = computed(() => {
		return md.render(inputValue.value)
	})

	// 插入语法和光标
	const focus = ref(false)
	const cursor = ref(-1)
	const setStyle = async tool => {
		if (tool.list) {
			return
		}
		if (tool.show) {
			// 展示弹窗
			if (tool.show === 'images') {
				attachmentRef.value.openDrawer(true) // 打开附件库
			} else {
				showModal.value = {
					...tool
				}
			}
			return
		}
		focus.value = true
		await nextTick() // 等待光标生效
		// 获取当前光标位置
		uni.getSelectedTextRange({
			success: async res => {
				focus.value = false // 先隐藏光标，不然会闪一下
				putStyle(tool, res.start, res.end)
				await nextTick()
				putSelection(tool, res.start, res.end)
			},
			fail: err => {
				console.log(err) // 可能是浏览器不支持
			}
		})
	}

	// 设置光标位置
	const putSelection = (tool, start, end) => {
		let count
		if (tool.end) {
			count = start === end ? start + tool.start.length : end + tool.end.length // 双标签
		} else {
			count = start + tool.start.length // 单标签
		}
		// 加个延迟，不然光标会跑到最后
		setTimeout(async () => {
			focus.value = true
			cursor.value = count
			await nextTick()
			scrollTop.value = scrollTop.value + 0.01 // 还原滚动条位置
		}, 100)
	}

	// 在字符串头尾插入 markdown 语法
	const putStyle = (tool, start, end) => {
		let text = inputValue.value
		if (tool.end) {
			inputValue.value = text.slice(0, start) + tool.start + text.slice(start, end) + tool.end + text.slice(end)
		} else {
			inputValue.value = text.slice(0, start) + tool.start + text.slice(start)
		}
		putHistory()
	}

	// 恢复光标位置
	const blur = () => {
		focus.value = false
		cursor.value = -1
	}

	// 弹窗组件
	const showModal = ref({
		show: '',
		title: ''
	})
	const formRef = ref()
	const confirmModal = () => {
		setStyle(formRef.value.getStyle())
	}

	// 选中附件
	const attachmentRef = ref()
	const selected = item => {
		setStyle({
			start: `![${item.name}](${item.url})\n`
		})
	}

	// 把以下方法暴露给父组件
	const setValue = value => {
		inputValue.value = value // 初始化输入值
		putHistory()
	}
	const getHtml = () => {
		return compiledMarkdown.value // 返回 html 内容
	}
	const getValue = () => {
		return inputValue.value // 返回输入的内容
	}
	defineExpose({
		setValue,
		getHtml,
		getValue
	})
</script>

<style lang="scss">
	$border-style: 1px solid #e6e6e6;

	.bg-editor {
		width: 100%;
		height: 80vh;
		margin: 30px 0;
		border-radius: 2px;
		background-color: #ffffff;

		&.only-edit {
			.editor-html {
				display: none;
			}

			.editor-textarea {
				width: 100% !important;
			}
		}

		&.only-html {

			.editor-tools,
			.editor-textarea {
				display: none;
			}

			.editor-html {
				width: 100% !important;
			}
		}

		&.expand {
			position: fixed;
			top: 0;
			left: 0;
			z-index: 99999999;
			margin-top: 0;
			height: 100%;

			.editor-textarea,
			.editor-html {
				height: 90vh !important; // 全屏高度 
			}
		}

		.editor-tools {
			display: flex;
			flex-wrap: wrap;
			width: 100%;
			padding: 5px;
			border: $border-style;

			.divider-l,
			.divider-r {
				position: relative;

				&::after {
					content: '';
					position: absolute;
					top: 15%;
					height: 70%;
					border-right: $border-style;
				}
			}

			.divider-r {
				margin-right: 10px !important;

				&::after {
					right: -6px;
				}
			}

			.divider-l {
				margin-left: 10px !important;

				&::after {
					left: -6px;
				}
			}

			.tool-box {
				display: flex;
				align-items: center;
				justify-content: center;
				border-radius: 4px;
				margin: 0 2px;
				padding: 5px 8px;
				position: relative;

				&:hover {
					background-color: #e2e2e2;

					.list-box {
						visibility: visible;
						transition: visibility 0s 0.2s;
					}
				}

				.list-box {
					visibility: hidden;
					position: absolute;
					top: 120%;
					right: -80%;
					width: 80px;
					border-radius: 4px;
					border: $border-style;
					background-color: #ffffff;
					z-index: 99;

					&::after {
						content: '';
						position: absolute;
						top: -20%;
						left: 0;
						width: 100%;
						height: 20%;
						opacity: 0;
					}

					.item {
						display: block;
						width: 100%;
						font-size: 13px;
						padding: 5px 0;
						text-align: center;
						word-break: keep-all;
						color: #222222;
						cursor: pointer;

						&:hover {
							background-color: #e2e2e2;
						}
					}
				}
			}
		}

		.editor-main {
			display: flex;
			position: relative;

			.editor-textarea,
			.editor-html {
				width: 50%;
				height: 70vh;
				padding: 10px;
				box-sizing: content-box;
				border: $border-style;
			}

			.editor-textarea {
				font-size: 15px;
				line-height: 20px;
			}

			.markdown-body,
			.textarea {
				width: 100%;
				height: 100%;
			}
		}

		.close-html {
			position: absolute;
			display: flex;
			align-items: center;
			justify-content: center;
			top: 30px;
			right: 30px;
			width: 26px;
			height: 26px;
			border-radius: 26px;
			background-color: #cccccc;
		}
	}
</style>